﻿using AutoMapper;
using Newtonsoft.Json;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Zocono.Infrastructure.Common;
using Zocono.WCS.Domain.DomainRepository;
using Zocono.WCS.Domain.EntityInfo;
using Zocono.WCS.Domain.LogDomain;
using Zocono.WCS.Domain.PlcBytesObject;
using Zocono.WCS.Domain.PlcConnectPoolDomain;
using Zocono.WCS.Domain.ProcessModuleDomain;
using Zocono.WCS.Infrastructure.ApllicationDto.Enums;
using Zocono.WCS.Infrastructure.ApllicationDto.WCSTaskDto;
using Zocono.WCS.Infrastructure.PlcCommon;
using Zocono.WCS.WMSApiDomain.WebApi;

namespace Zocono.WCS.Domain.EquipmentProcessDomain.PlanarGeneralMessage
{
    /// <summary>
    /// 四面码校验
    /// </summary>
    public class FourContainerCodeCheckService : IPlanarGeneralPlcActionProcessService
    {
        private readonly IRedisHelper _IRedisHelper;
        private readonly ISnowflake _snowflake;
        private readonly ILogUtil _logUtil;
        private readonly IPlcConnectionPool _plcConnectionPool;
        private readonly IPlcWriteDbInfoRepository _plcWriteDbInfoRepository;
        private readonly IPlcConnectionInfoRepository _PlcConnectionInfoRepository;
        private readonly IPlcConnectionStateInfoRepository _IPlcConnectionStateInfoRepository;
        private readonly IRunLogInfoService _IRunLogInfoService;
        private readonly IPlcReadDbInfoRepository _PlcReadeDbInfoRepository;

        public FourContainerCodeCheckService(IRedisHelper iRedisHelper, ISnowflake snowflake, ILogUtil logUtil,
            IPlcConnectionPool plcConnectionPool
            , IPlcWriteDbInfoRepository plcWriteDbInfoRepository
             , IPlcConnectionInfoRepository plcConnectionInfoRepository
           , IPlcConnectionStateInfoRepository plcConnectionStateInfoRepository
            , IRunLogInfoService runLogInfoService, IPlcReadDbInfoRepository plcReadDbInfoRepository)
        {
            _IRedisHelper = iRedisHelper;
            _snowflake = snowflake;
            _logUtil = logUtil;
            _plcConnectionPool = plcConnectionPool;
            _plcWriteDbInfoRepository = plcWriteDbInfoRepository;
            _PlcConnectionInfoRepository = plcConnectionInfoRepository;
            _IPlcConnectionStateInfoRepository = plcConnectionStateInfoRepository;
            _IRunLogInfoService = runLogInfoService;
            _PlcReadeDbInfoRepository = plcReadDbInfoRepository;
        }

        public async Task ProcessActionMessageAsync(BytesObjectBase plcActionMessage, BytesObjectBase wcsActionMessage, EquipmentDbObject planarEquipmentDbObject/*, BytesObjectBase plcStateMassge*/)
        {
            /*
             *  需求说明
                1.判断plc状态与设备状态（仅报警处理）
                2.根据任务状态【待执行、执行中】查询wcs托盘上架任务是否存在?（存在则直接回复正常报文 否则请求wms下发入库上架任务）
                  NA说明：1) 根据wms下发任务生成wcs任务
                          2）计算wcs任务路径id【RouteInfo】第一条路径明细【RouteDetailInfo】作为wcs子任务【WorkTaskInfo】Cuttent部分
                          3）更新wms任务状态为执行中
                3.逻辑处理完成 则开始写入Plc 写入成功则处理WCS操作数据库业务 否则不操作
                  说明：1）默认wcs ActionType 跟随Plc ActionType值 即Plc ActionType为 1 则wcs ActionType回写也为 1 
                        2）针对Plc离线或设备状态报警程序直接return
                         plc动作报文托盘号为空或wms请求巷道任务失败 则响应握手类型 =100表示报警
             */


            var plcMessage = plcActionMessage as PlanarGeneralPlcActionBO;//Plc的动作报文
            var wcsMessage = wcsActionMessage as PlanarGeneralWcsActionBO;//Wcs的动作报文
            plcMessage.PalletBarcode = plcMessage.PalletBarcode.Trim();

            RunLogInfo runLogInfo = new();
            runLogInfo.ContainerBarcode = plcMessage.PalletBarcode;
            runLogInfo.EquipmentCode = plcMessage.EquipmentCode;
            runLogInfo.RunType = "四码校验校验";

            //var plcStateBo = plcStateMassge as PlcOnLineStateBO;//Plc在线离线报文 没有设备信息
            PlanarGeneralWriteWcsAction writeBo = null;//写入Plc的报文
            PlcConnectionS7 plcConn = _plcConnectionPool.S7GetWritePlcConnection(plcMessage.PlcConnectionID);//plc写入连接（不锁定）

            if (plcConn == null)
            {
                //runLogInfo.RunLog = $"获取连接({writeBo.PlcConnectionID})失败";
                //await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                _logUtil.Error($"四码校验校验：托盘【{plcMessage.PalletBarcode}】在设备【{plcMessage.EquipmentCode}】四码校验校验：plc连接池无数据");
                return;
            }

            #region Plc离线判断&设备报警判断
            runLogInfo.RunLog = "四码校验校验";
            //1. 验证plc是否在线
            var plcIPStates = await _IPlcConnectionStateInfoRepository.GetFirstAsync(l => l.PlcConnectionID == plcMessage.PlcConnectionID && l.Connected == true);
            if (plcIPStates == null)
            {
                //runLogInfo.RunLog = $"离线：请求分配失败：{plcConn.IP}:plc已离线";
                //await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                _logUtil.Error($"四码校验校验：托盘【{plcMessage.PalletBarcode}】在设备【{plcMessage.EquipmentCode}】四码校验校验：请求分配失败：{plcConn.IP}:plc已离线");
                return;
            }


            #endregion

            #region plc写入连接获取&写入报文构建
            //Plc连接Id
            //long plcId = wcsMessage.PlcConnectionID;
            //写入plc的报文配置获取
            var objectType = typeof(PlanarGeneralWriteWcsAction).Name;
            var writeConfig = await _plcWriteDbInfoRepository.GetFirstAsync(s => s.EquipmentCode == plcMessage.EquipmentCode && s.ObjectType == objectType);
            if (writeConfig == null)
            {

                //runLogInfo.RunLog = "获取PLC写入配置失败";
                //await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                _logUtil.Error($"四码校验校验：托盘【{plcMessage.PalletBarcode}】在设备【{plcMessage.EquipmentCode}】四码校验校验获取PLC写入配置失败！请检查写DB配置！");
                return;
            }

            //构建写入报文
            writeBo = new PlanarGeneralWriteWcsAction(
                writeConfig.PlcConnectionId,
                writeConfig.ID,
                writeConfig.DbCode,
                writeConfig.DbStart,
                objectType,
                plcMessage.EquipmentCode.ToInt32(),
                //Convert.ToInt32(writeConfig.EquipmentCode),
                plcMessage.ActionType,
                plcMessage.ToEquipmentCode,
                taskCode: plcMessage.TaskCode,
                palletBarcode: plcMessage.PalletBarcode,
                plcMessage.PalletType,
                0);

            //校验plc信息是否有托盘码
            if (string.IsNullOrEmpty(plcMessage.PalletBarcode) || plcMessage.PalletBarcode.Length != 10)
            {
                writeBo.AlrmCode = 102;
                writeBo.ActionType = 99;
                var flag = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                _IRedisHelper.SetStringKey($"PLC-{plcMessage.EquipmentCode}", plcMessage.EquipmentCode.ToString() + plcMessage.AlrmCode, TimeSpan.FromSeconds(10));
                return;
            }


            //托盘类型
            switch (writeBo.PalletBarcode.Substring(1, 1))
            {
                case "A":
                    writeBo.PalletType = 2;
                    break;
                case "B":
                    writeBo.PalletType = 1;
                    break;
                case "C":
                    writeBo.PalletType = 3;
                    break;
            }
            #endregion


            #region 

            //校验条码
            if (plcMessage.ActionType == 70)
            {
                //获取四码DB块信息，进行读取
                var plcConfigput = await _PlcReadeDbInfoRepository.GetFirstAsync(l => l.EquipmentCode == plcMessage.EquipmentCode + "-SM");


                byte[] bytes = await plcConn.ReadDbAsync(plcConfigput.DbCode, plcConfigput.DbStart, plcConfigput.DbLength);
                if (bytes == null)
                {
                    //runLogInfo.RunLog = $"读DB失败";
                    //await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                    _logUtil.Error($"四码校验校验：四码校验校验，PLC设备【{plcMessage.EquipmentCode}】读取四码DB块信息条码失败！");
                    return;
                }

                PlanarContainerCodesBO plcActionBO = new();
                plcActionBO.Serialize(plcConfigput.PlcConnectionID, plcConfigput.ID, plcConfigput.DbStart, plcConfigput.DbLength, plcConfigput.DbStart, plcConfigput.DbLength, "PlanarContainerCodesBO", plcConfigput.EquipmentCode, bytes);//主要是bytes转化成明文
                plcActionBO.PalletBarcode = plcActionBO.PalletBarcode.Trim();
                plcActionBO.PalletBarcode2 = plcActionBO.PalletBarcode2.Trim();
                plcActionBO.PalletBarcode3 = plcActionBO.PalletBarcode3.Trim();
                plcActionBO.PalletBarcode4 = plcActionBO.PalletBarcode4.Trim();

                //托盘码为空
                if (string.IsNullOrEmpty(plcActionBO.PalletBarcode) || string.IsNullOrEmpty(plcActionBO.PalletBarcode2) || string.IsNullOrEmpty(plcActionBO.PalletBarcode3) || string.IsNullOrEmpty(plcActionBO.PalletBarcode4))
                {
                    writeBo.AlrmCode = 135;
                    writeBo.ActionType = 99;
                    var flag = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                    _IRedisHelper.SetStringKey($"PLC-{plcMessage.EquipmentCode}", plcMessage.EquipmentCode.ToString() + plcMessage.AlrmCode, TimeSpan.FromSeconds(10));
                    return;
                }
                //托盘码错误
                if (plcActionBO.PalletBarcode.Length != 10 || plcActionBO.PalletBarcode2.Length != 10 || plcActionBO.PalletBarcode3.Length != 10 || plcActionBO.PalletBarcode4.Length != 10)
                {
                    writeBo.AlrmCode = 136;
                    writeBo.ActionType = 99;
                    var flag = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                    _IRedisHelper.SetStringKey($"PLC-{plcMessage.EquipmentCode}", plcMessage.EquipmentCode.ToString() + plcMessage.AlrmCode, TimeSpan.FromSeconds(10));
                    return;
                }

                //托盘码是否一致判断
                if (plcActionBO.PalletBarcode == plcActionBO.PalletBarcode2 && plcActionBO.PalletBarcode == plcActionBO.PalletBarcode3 && plcActionBO.PalletBarcode == plcActionBO.PalletBarcode4)
                {
                    writeBo.ActionType = 70;
                    var flag = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                    if (flag)
                    {
                        runLogInfo.RunLog = $"四码校验校验：托盘【{plcMessage.PalletBarcode}】四码校验校验成功，PLC设备【{plcMessage.EquipmentCode}】写入PLC成功！写入报文：{JsonConvert.SerializeObject(writeBo)}";
                        await _IRunLogInfoService.AddRunLogInfoAsync(runLogInfo);
                    }
                    else
                    {
                        _logUtil.Error($"四码校验校验：四码校验校验，PLC设备【{plcMessage.EquipmentCode}】四码校验校验成功，写入报文失败：{JsonConvert.SerializeObject(writeBo)}");
                    }
                }
                else
                {
                    writeBo.AlrmCode = 137;
                    writeBo.ActionType = 71;
                    var flag = await plcConn.WriteDbAsync(writeBo.DbCode, writeBo.DbStart, writeBo.Deserialize());
                    _logUtil.Error($"四码校验校验：四码校验校验，PLC设备【{plcMessage.EquipmentCode}】四码校验失败，写入报文：{JsonConvert.SerializeObject(writeBo)}");
                    //_IRedisHelper.SetStringKey($"PLC-{plcMessage.EquipmentCode}", plcMessage.EquipmentCode.ToString() + plcMessage.AlrmCode, TimeSpan.FromSeconds(10));
                    return;
                }

            }

            #endregion

            

        }

        /// <summary>
        /// 根据写入db配置将报文写入plc
        /// </summary>
        /// <param name="equipmentCode">设备编号</param>
        /// <param name="plcConnectionId"></param>
        /// <param name="bytes"></param>
        /// <returns></returns>
        private async Task<bool> WriteDbAsync(string equipmentCode, long plcConnectionId, byte[] bytes)
        {
            var plcConn = _plcConnectionPool.S7GetWritePlcConnection(plcConnectionId);
            //根据设备编号获取写入配置
            var writeConfig = await _plcWriteDbInfoRepository.GetSingleAsync(s => s.EquipmentCode == equipmentCode
            && s.PlcConnectionId == plcConnectionId && s.ObjectType == typeof(PlanarGeneralWriteWcsAction).Name);
            //写入报文处理
            bool isSend = await plcConn.WriteDbAsync(writeConfig.DbCode, writeConfig.DbStart, bytes);
            if (isSend)
                _logUtil.Info($"设备号{equipmentCode}plc连接ID{plcConnectionId} 写入报文成功");
            else
                _logUtil.Info($"设备号{equipmentCode}plc连接ID{plcConnectionId} 写入报文失败");
            return isSend;
        }

    }
}
